const { ccclass, property } = cc._decorator;

enum RunningState {
    Idle,
    Running,
};

enum ShapeType {
    Rect = 0,
    Circle
};

@ccclass
export default class BorderShapeCycler extends cc.Component {

    @property({
        type: cc.Enum(RunningState)
    })
    mode: RunningState = RunningState.Idle
    @property({
        type: cc.Enum(ShapeType)
    })
    shape: ShapeType = ShapeType.Rect
    @property(cc.Node)
    cycleNod: cc.Node = null
    @property
    auto: boolean = false
    @property
    loop: boolean = false
    @property(cc.Node)
    particleNodes: cc.Node = null
    @property
    actorLim: number = 2
    @property
    actorSpeed: number = 10

    _actorParticles: cc.ParticleSystem[] = []
    _timePast: number = 0
    _cycleCubeLim: cc.Vec2 = cc.v2()
    _cycleRadius: number = 1
    _angleDistance: number = 360
    _offsetX: number = 0
    _offsetXLim: number = 1
    _offsetContain: cc.Vec2[] = []
    _offsetValueChange: number[] = []
    start() {
        this.Reset()
    }
    Reset(): void {
        switch (this.shape) {
            case ShapeType.Rect:
                this._cycleCubeLim = cc.v2(this.cycleNod.getContentSize().width / 2, this.cycleNod.getContentSize().height / 2)
                this._cycleRadius = this._cycleCubeLim.mag()
                break
            case ShapeType.Circle:
                this._cycleCubeLim = cc.v2(this.cycleNod.getContentSize().width / 2, this.cycleNod.getContentSize().height / 2)
                this._cycleRadius = this._cycleCubeLim.x
                break
        }

        this._offsetXLim = this.cycleNod.getContentSize().width / 2
        this._angleDistance = 360 / this.actorLim
        this._actorParticles.push(this.particleNodes.children[0].getComponent(cc.ParticleSystem))
        let pcCount = this.particleNodes.childrenCount;
        for (let i = 1; i < this.actorLim; i++) {
            let tObj = i < pcCount ? this.particleNodes.children[i] : cc.instantiate(this.particleNodes.children[0])
            i >= pcCount && tObj.setParent(this.particleNodes)
            this._actorParticles.push(tObj.getComponent(cc.ParticleSystem))
        }
        if (this.loop) {
            this._actorParticles.forEach((v) => {
                v.duration = -1
            })
        }
        if (this.auto) {
            this.mode = RunningState.Running
            this._actorParticles.forEach((v) => {
                v.resetSystem()
            })
        }
    }
    update(dt: number): void {
        this._behave(dt)
    }
    _behave(dt: number) {
        switch (this.mode) {
            case RunningState.Running:
                this._timePast += dt * this.actorSpeed
                this._offsetX = Math.sin(this._timePast) * this._cycleRadius
                break
            default: return
        }
        this._calculateOffsetForAll()
    }
    _calculateOffsetForAll() {
        this._offsetContain.splice(0)
        let tposFirst = cc.v2(this._offsetX, Math.sqrt(this._cycleRadius ** 2 - this._offsetX ** 2))
        if (this._offsetValueChange[0] == null) {
            this._offsetValueChange[0] = tposFirst.x
        } else if (tposFirst.x < this._offsetValueChange[0]) {
            tposFirst.y *= -1
        }
        this._offsetValueChange[0] = tposFirst.x

        this._offsetContain.push(tposFirst)
        for (let i = 1; i < this.actorLim; i++) {
            let tPosTemp = tposFirst.rotate(cc.misc.degreesToRadians(i * this._angleDistance))
                , tTag = cc.v2(0, 1).rotate(cc.misc.degreesToRadians(i * this._angleDistance))
            if (this._offsetValueChange[i] == null) {
                this._offsetValueChange[i] = tPosTemp.x
            } else if (tPosTemp.x < this._offsetValueChange[i]) {
                tPosTemp.y *= (tTag.x - tTag.y) / Math.abs(tTag.x - tTag.y)
            }
            this._offsetValueChange[i] = tPosTemp.x
            this._offsetContain.push(tPosTemp)
        }

        this._offsetContain.forEach((v, k) => {
            this._offsetContain[k] = cc.v2(cc.misc.clampf(v.x, -this._cycleCubeLim.x, this._cycleCubeLim.x)
                , cc.misc.clampf(v.y, -this._cycleCubeLim.y, this._cycleCubeLim.y))
        })

        this._actorParticles.forEach((v, k) => {
            v.node.setPosition(this._offsetContain[k])
        })
    }
}
